package com.googlecode.archimedes.maven;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;

import com.googlecode.archimedes.filegen.controller.ArchimedesController;
import com.googlecode.archimedes.filegen.controller.ArchimedesControllerFactory;
import com.googlecode.archimedes.filegen.controller.implementation.ApplicationBridge;
import com.googlecode.archimedes.filegen.model.Application;
import com.googlecode.archimedes.filegen.model.Entity;
import com.googlecode.archimedes.filegen.model.Enumeration;
import com.googlecode.archimedes.filegen.model.Interface;
import com.googlecode.archimedes.filegen.template.Template;
import com.googlecode.archimedes.parser.DataParser;

/**
 * An implementatin of an Maven build plugin that will run Archimedes. This task is specific to creating an Archimedes application by using the
 * Application object. It makes use of DataParser to process the provided XML file to turn the XML structure into Entities, Members, Templates and
 * Relationships.
 * 
 * @author Erik Gustavson
 * @version $Id: ArchimedesTask.java 2088 2006-03-16 01:56:24 +0000 (Thu, 16 Mar 2006) erik $
 * @see DataParser Created on Nov 28, 2003 TODO: Write Unit test
 * @goal generate
 */
public class ArchimedesTask extends AbstractMojo {

	private Log		log				= LogFactory.getLog(this.getClass());

	/**
	 * @parameter default-value="${basedir}/src/main/java"
	 */
	private File	outputdir;

	/**
	 * @parameter default-value="${basedir}/src/main/resources/templates"
	 */
	private File	templatedir;

	/**
	 * @parameter default-value="${basedir}/src/main/resources/model.xml"
	 */
	private File	xmlfile;

	/**
	 * @parameter default-value="http://archimedes-codegen.googlecode.com/svn/trunk/src/main/resources/archimedes_0_2.dtd"
	 * 
	 * parameter default-value="${basedir}/src/main/resources/archimedes_0_2.dtd"
	 */
	private String	dtdurl;

	/**
	 * @parameter default-value="false" expression="${archimedes.force.overwrite}"
	 */
	private boolean	forceOverwrite	= false;

	/**
	 * The method that is called from the ANT script. This method starts the whole process.
	 * 
	 * @see org.apache.tools.ant.Task#execute()
	 */
	public void execute() throws MojoExecutionException {

		try {
			
			//listen for some system properties
			
			// test all the parameters
			validate();

			// execute
			DataParser parser = process(); // read the xml

			// Assembles and builds everything
			buildApplication(parser);

		} catch (Exception e) {
			e.printStackTrace();
			throw new MojoExecutionException("Unable to create Archimedes output.", e);
		}
	}

	/**
	 * A validation routine to check that all the provided inputs are valid.
	 * 
	 * @throws Exception
	 */
	private void validate() throws Exception {
		// XML & DTD files
		if (getXmlfile() == null || getXmlfile().exists() == false) {
			throw new MojoExecutionException("The XML model definition [" + getXmlfile() + "] does not exist.");
		}

		// outputdir
		if ((getOutputdir().exists() == false) && (getOutputdir().isDirectory())) {
			throw new MojoExecutionException("The output directory [" + getOutputdir() + "] does not exist or is not a directory.");
		}

		// templatedir
		if ((getTemplatedir().exists() == false) && (getTemplatedir().isDirectory())) {
			throw new MojoExecutionException("The template directory [" + getTemplatedir() + "] does not exist or is not a directory.");
		}
	}

	/**
	 * Runs the DataParser to process the XML input.
	 * 
	 * @return a DataParser object that has been filled out.
	 * @throws Exception
	 */
	private DataParser process() throws Exception {
		DataParser parser = new DataParser();
		parser.parse(getXmlfile(), new URL(getDtdurl()));// getDtdfile().toURI().toURL());
		return parser;
	}

	/**
	 * Actually creates the application.
	 * 
	 * @param parser
	 *            A DataParser that contains all the necessary information.
	 * @throws Exception
	 */
	private void buildApplication(DataParser parser) throws Exception {
		// get the controller
		ArchimedesController controller = ArchimedesControllerFactory.getController();

		// Setup the application bridge
		ApplicationBridge bridge = new ApplicationBridge();
		bridge.setLoggingDirectory(System.getProperty("java.temp.dir"));
		bridge.setOutputDirectoy(getOutputdir().getAbsolutePath());
		bridge.setResourceDirectory(getTemplatedir().getAbsolutePath());
		bridge.setTemplates(parser.getTemplates());

		if (isForceOverwrite()) {
			log.warn("Forcing file overwrites -- template settings will be ignored!");
			for (Template template : bridge.getTemplates()) {
				template.setOverwriteAllowed(true);
			}
		}

		// Create an Application object
		Application app = parser.getApplication();

		// Add all the entities to the Application object
		List<Entity> entities = new ArrayList<Entity>(parser.getEntities().values());
		app.setEntities(entities);

		// Add all the interfaces to the Application object
		List<Interface> interfaces = new ArrayList<Interface>(parser.getInterfaces().values());
		app.setInterfaces(interfaces);

		// Add all the enumerations to the Application object
		List<Enumeration> enumerations = new ArrayList<Enumeration>(parser.getEnumerations().values());
		app.setEnumerations(enumerations);

		// Add all the tabs to the Application object
		app.setTabs(parser.getTabs());

		// Set the Application object onto the GenerationBridge
		bridge.setApp(app);

		// generate the AppBridge
		controller.generate(bridge);
	}

	public String getDtdurl() {
		return this.dtdurl;
	}

	public File getOutputdir() {
		return this.outputdir;
	}

	public File getTemplatedir() {
		return this.templatedir;
	}

	public File getXmlfile() {
		return this.xmlfile;
	}

	/**
	 * @param forceOverwrite
	 *            the forceOverwrite to set
	 */
	public void setForceOverwrite(boolean forceOverwrite) {
		this.forceOverwrite = forceOverwrite;
	}

	/**
	 * @return the forceOverwrite
	 */
	public boolean isForceOverwrite() {
		return forceOverwrite;
	}
}
